package org.duang.kit;

import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.duang.aop.ProxyManager;
import org.duang.config.Config;
import org.duang.config.InstanceFactory;
import org.duang.core.Controller;
import org.duang.db.annotation.Bean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ClassKit {
	 private static Logger logger = LoggerFactory.getLogger(ClassKit.class);
	 private static final Map<String, Class<? extends Controller>> classControllerMaps = new HashMap<String, Class<? extends Controller>>();
	 private static final ConcurrentMap<String, Constructor<?>> constMapping = new ConcurrentHashMap<String, Constructor<?>>();
	 private static final ConcurrentMap<String, Field[]> fieldMapping = new ConcurrentHashMap<String, Field[]>();
		
	 public static void scanClass2Map(){
		 scanClass2File(PathKit.getBasePackagePath());		//扫描class文件
		 scanClass2Jar(PathKit.getLibPath());		//扫描jar文件
		 if(classControllerMaps == null || classControllerMaps.isEmpty()){ throw new NullPointerException("classControllerMaps is null...");}
		 InstanceFactory.setRoutesMaps(classControllerMaps);
	 }
	 
	 private static FileFilter ClassFileFilter(final File dir, final String extName){
		 return new FileFilter() {
			 public boolean accept(File file) {
				 if(".class".equalsIgnoreCase(extName)) {
					 return ( file.isFile() && file.getName().endsWith(extName) ) || file.isDirectory();
				 } else if(".jar".equalsIgnoreCase(extName)) {
					 return ( file.isFile() && file.getName().endsWith(extName) ) || file.isFile();
				 } else {
					 throw new IllegalArgumentException();
				 }
			 }
		 };
	 }
	 
	 private static void scanClass2File(String folderPath) {
		 File dir = new File(folderPath);
		 if (!dir.exists() || !dir.isDirectory()) {	//不存在或不是目录
			 throw new IllegalArgumentException(dir.getAbsolutePath() + " is not exists or not is Directory!");
		 }	 
		 //遍历目录下的所有文件
		 File[] files = dir.listFiles(ClassFileFilter(dir,".class"));
		 for(int i=0; i<files.length; i++) {
			 File file = files[i];
			 if(file.isDirectory()){
				 scanClass2File(file.getAbsolutePath());
			 }else{
				 if(file.isFile()) {
					 String classPath = getClassAbsolutePath(file);					 
					 String classKey = file.getName();
					 fill(classKey, classPath);
				 }
			 }
		 }
	 }
	 
	 private static void scanClass2Jar(String folderPath) {
		//遍历目录下的所有Jar
		 File dir = new File(folderPath);
		 File[] files = dir.listFiles(ClassFileFilter(dir,".jar"));
		 String basePackPath = Config.getBasePackagePath();
		 try{
			 for(File file : files){
				 JarFile localJarFile = new JarFile(new File(file.getAbsolutePath()));
				 Enumeration<JarEntry> entries = localJarFile.entries();
				 while(entries.hasMoreElements()) {
					 JarEntry jarEntry = entries.nextElement();
					 String entryName = jarEntry.getName();
					 if(!jarEntry.isDirectory() && entryName.endsWith(".class")) {
						 String classPath = entryName.replaceAll("/", ".").substring(0, entryName.length()-6);
						 if(classPath.startsWith(basePackPath)){
							 String classKey = classPath.substring(classPath.lastIndexOf(".")+1, classPath.length());
							 fill(classKey+".class", classPath);
						 }
					 }
				 }
				 localJarFile.close();
			 }
		 } catch (Exception e) {
			 e.printStackTrace();
		 }
	 }
	 
	private static void fill(String classKey, String classPath) {
		 if(classKey.endsWith(Config.CONTROLLER_ENDWITH_NAME)) {
			 classKey = classKey.substring(0, classKey.indexOf(Config.CONTROLLER_ENDWITH_NAME));
			 if(!classKey.startsWith("/")){
				 classKey = "/"+classKey.toLowerCase();
			 }
			 if(!ToolsKit.isExist(classKey, classControllerMaps)){							 
				 classControllerMaps.put(classKey, extraction(Controller.class, classPath));
			 }
		 }
		 Class<?> cls = null;
		 if(classKey.endsWith(Config.SERVICE_ENDWITH_NAME)) {
			 cls = forName(classPath);
			 BeanKit.setBean(cls, (Object)ProxyManager.newProxyInstance(cls));
		 }
		 
		 if(classKey.endsWith(Config.LISTENER_ENDWITH_NAME)) {		
			 cls = forName(classPath);
			 BeanKit.setBean(cls, (Object)ProxyManager.newProxyInstance(cls));
		 }
	}
	 
	 private static Class<?> forName(String className){
		 try {
			return Class.forName(className);
		} catch (ClassNotFoundException e) {
			logger.warn(e.getMessage(), e);
			return null;
		}
	 }
	
	 
	 private static String getClassAbsolutePath(File file) {
		 String classPath = file.getAbsolutePath().replaceAll("\\\\", "/");
		 classPath =  classPath.substring(classPath.indexOf("/classes") + "/classes".length() + 1, classPath.length()-6);
		 classPath = classPath.replaceAll("/", ".");		 
		 return classPath;
	 }
	 
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private static <T> Class<? extends Controller> extraction(Class<T> cls, String classPath) {
		Class clazz = ObjectKit.newInstance(classPath).getClass();
		if(cls.isAssignableFrom(clazz) && cls != clazz){
			return (Class<? extends Controller>)clazz;
		}
		return clazz;
	 }
	/**
	 * 取得Bean的名字,如果有指定则用指定的,没有则用小写的类名作表名用
	 * @param cls
	 * @return
	 */
	public static String getBeanName(Class<?> cls) {
		return getBeanName(cls, false);
	}
	
	public static String getBeanName(Class<?> cls, boolean isLowerCase) {
		Bean bean = cls.getAnnotation(Bean.class);
		String name= ( null == bean )? getClassSimpleName(cls) : bean.name();
		return isLowerCase ? name.toLowerCase() : name;
	}
	
	/**
	 * 取类的简短名称，默认返回小写名称
	 * @param cls		类对象
	 * @return			小写简短名称
	 */
	public static String getClassSimpleName(Class<?> cls) {
		return getClassSimpleName(cls ,true);
	}
	
	public static String getClassName(Class<?> cls) {
		return getClassName(cls ,true);
	}
	
	public static String getClassName(Class<?> cls, boolean isLowerCase) {
		String name = cls.getName(); 
		return isLowerCase ? name.toLowerCase() : name;
	}
	
	/**
	 * 取类的简短名称
	 * @param cls			类对象
	 * @param isLowerCase	是否返回小写,true时返回小写	
	 * @return				简短名称
	 */
	public static String getClassSimpleName(Class<?> cls, boolean isLowerCase) {
		String name = cls.getSimpleName(); 
		return isLowerCase ? name.toLowerCase() : name;
	}
	
	@SuppressWarnings("rawtypes")
	public static boolean isExtends(Class<?> cls, String topClassName) {
		String clsName = cls.getCanonicalName();
		if("java.lang.Object".equals(clsName)) return true;
		Class parent = cls.getSuperclass();
		if(ToolsKit.isNotEmpty(parent)){
			String name = parent.getCanonicalName();
			if(name.equals(topClassName)) return true;
			while(ToolsKit.isNotEmpty(parent)){
				parent = parent.getSuperclass();
				if(parent == null) return false;
				name = parent.getCanonicalName();
				if(name.equals(topClassName)){
					return true;
				}
			}
		}
		return false;
	}
	 
	
	/**
	 * 根据class对象反射出所有属性字段，静态字段除外
	 * @param cls
	 * @return
	 */
	public static Field[] getFields(Class<?> cls){
		String key = getClassSimpleName(cls);
		Field[] field = null;
		if(fieldMapping.containsKey(key)){
			field = fieldMapping.get(key);
		}else{
			field = getAllFields(cls);
			fieldMapping.put(key, field);
		}
		return (null == field) ? null : field;
	}
	
	/**
	 * 取出类里的所有字段
	 * @param cls
	 * @return	Field[]
	 */
	private static Field[] getAllFields(Class<?> cls) {
		List<Field> fieldList = new ArrayList<Field>();
		fieldList.addAll(filterFields(cls.getDeclaredFields()));
		Class<?> parent = cls.getSuperclass();
		//查找父类里的属性字段
		while(null != parent && parent != Object.class){
			fieldList.addAll(filterFields(parent.getDeclaredFields()));
			parent = parent.getSuperclass();
		}
		return fieldList.toArray(new Field[fieldList.size()]);
	}
	
	/**
	 * 过滤静态方法
	 * @param fields
	 * @return
	 */
	private static List<Field> filterFields(Field[] fields){
		List<Field> result = new ArrayList<Field>();
		for (Field field : fields) {
			if(!Modifier.isStatic(field.getModifiers())){		//静态字段不取
				field.setAccessible(true);	//设置可访问私有变量
				result.add(field);
			}
		}
		return result;
	}
	
	public static <T> T createConst(Class<T> cls){
        T obj = null;
        Constructor<T> cons = getConst(cls);
        Object[] args = null;
        try {
            obj = cons.newInstance(args);		//反射执行空的构造函数
        } catch (Exception ex) {
            logger.error("ClassUtil.createConst(): Something is wrong when create the new instance", ex);
        }
        return obj;
    }
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private static <T> Constructor<T> getConst(Class<T> cls){
		String key = getClassName(cls);	
		if(constMapping.containsKey(key)){
			return (Constructor<T>)constMapping.get(key);
		}else{
			Constructor<?> cons = null;
			Class[] types = null;
			try {
				cons = cls.getConstructor(types);
				constMapping.putIfAbsent(key, cons);
			} catch (SecurityException e) {
				logger.error("ClassUtil.getConst(): get constructor is security fail", e);
			} catch (NoSuchMethodException e) {
				logger.error("ClassUtil.getConst(): get constructor is no such method fail", e);
			}
			return (Constructor<T>)cons;
		}
	}
	
	 
}